﻿using System;
using System.Diagnostics.CodeAnalysis;
using CultivationSkills.Utils;
using ThunderRoad;
using UnityEngine;

namespace CultivationSkills.Skills;

[SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Global")]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
public class SpellThousandSwordsTechnique : SpellCastCharge
{
    public static ModOptionInt[] MaxCountOptions = ModOptionInt.CreateArray(1, 501, 10);

    [ModOption(name = "Shoot Damages Max Count", order = 1, category = "Spell Settings",
        interactionType = ModOption.InteractionType.Slider, valueSourceName = "MaxCountOptions",
        saveValue = true)]
    public static int MaxShoot = 10;

    [ModOption(name = "Down Shoot Distance", order = 2, category = "Spell Settings",
        interactionType = ModOption.InteractionType.Slider, valueSourceName = "MaxCountOptions",
        saveValue = true)]
    public static int DownShootDistance = 5;

    private float _maxCountSeconds = 6;
    public string shootItemId = SwordShoot.DefaultItemId;
    private Transform _spawnerPoint;
    private AudioSource _chargeSound;
    private bool _usable = false;
    private RagdollHand _casterRagdollHand;
    private int _chargeCounter = 0;

    public override void Load(SpellCaster caster)
    {
        base.Load(caster);

        Debug.Log("Load Spell");
        _casterRagdollHand = caster.ragdollHand;
        if (!_casterRagdollHand?.creature)
        {
            _usable = false;
            Debug.LogWarning("No Point To Spawn!");
            return;
        }

        _spawnerPoint = _casterRagdollHand.creature.centerEyes;
        var clip = MyAssets.GetClip(MyAudios.Charge);
        if (clip)
        {
            var audioSources = _casterRagdollHand.creature.gameObject.GetComponents<AudioSource>();
            if (audioSources != null && audioSources.Length > 0)
            {
                _chargeSound = Array.Find(audioSources,
                    audio => audio.clip == clip);
            }

            if (!_chargeSound)
            {
                _chargeSound = _casterRagdollHand.creature.gameObject.AddComponent<AudioSource>();
                _chargeSound.clip = clip;
            }

            _maxCountSeconds = _chargeSound.clip.length;
        }

        _usable = true;
    }

    private float _lastCntTime = 0;
    private int cnt = 0;

    public override void FixedUpdateCaster()
    {
        base.FixedUpdateCaster();
        if (!spellCaster.isFiring || Time.time - _lastCntTime < 1f) return;
        if (_chargeCounter < _maxCountSeconds)
        {
            readyEffectData?.Spawn(spellCaster.magicSource.TransformPoint(spellCaster.magicOffset),
                Quaternion.identity, this.spellCaster.magicSource, (CollisionInstance)null, true,
                (ColliderGroup)null, false, 1f, 1f)?.Play(skipLoops: true);
            _chargeCounter++;
            Debug.Log("_chargeCounter:" + _chargeCounter);
        }
        else
        {
            Debug.Log("结束！");
            _casterRagdollHand.caster.Fire(false);
        }

        _lastCntTime = Time.time;
    }

    public override void Fire(bool active)
    {
        Log("Fire:" + active);
        base.Fire(active);
        if (active)
        {
            _chargeCounter = 0;
            _chargeSound.Play();
        }
        else
        {
            Billing(false);
        }
    }

    private void Billing(bool shoot,
        Vector3 throwVector = default)
    {
        _chargeSound.Stop();
        if (_chargeCounter == 0)
        {
            return;
        }

        if (_usable && shoot)
        {
            var count = GetCount();
            DoSpawn(count, throwVector);
        }

        _chargeCounter = 0;
    }

    private static readonly LayerMask AllTargetMask = LayerMask.GetMask(
        "PhysicObject",
        "DroppedItem",
        "MovingItem",
        "Ragdoll",
        "SkyDome",
        "MovingObjectOnly",
        "BodyLocomotion",
        "ItemAndRagdollOnly",
        "NPC",
        "Zone"
    );

    private void DoSpawn(int count, Vector3 throwVector)
    {
        var directionType = GameUtils.GetGestureDirection(throwVector);
        Log($"Throw：{throwVector}, directionType: {directionType}");
        // 纠正
        var direction = Vector3.zero != throwVector && directionType != GestureDirectionType.Downward
            ? throwVector.normalized
            : Player.local.head.transform.forward;
        var findHit = GameUtils.FindForwardHit(_casterRagdollHand.transform.position, direction,
            GameUtils.NpcTargetMask, out var hit);
        if (!findHit)
        {
            Debug.Log("未找到敌人!");
            findHit = Physics.Raycast(_casterRagdollHand.transform.position, direction, out hit,
                20, AllTargetMask);
            if (findHit)
            {
                Debug.Log($"any hit:{hit.collider.gameObject.name},{hit.point},{LayerMask.LayerToName(hit.collider.gameObject.layer)}");
            }
        }

        var fixTarget = findHit ? hit.point : Vector3.zero;

        switch (directionType)
        {
            case GestureDirectionType.PushOrTilt:
                // 小于4时手前中生成
                var pos = count < 4
                    ? _casterRagdollHand.transform.position + throwVector.normalized / 2
                    : _spawnerPoint.position + new Vector3(0, 0.5f, 0);
                SwordShoot.ShootMulti(count, pos, direction, shootItemId, fixTarget);
                break;
            case GestureDirectionType.Upward:
            {
                var itemData = SwordShoot.GetItemData(shootItemId);
                if (itemData == null)
                {
                    Debug.LogWarning("没有找到生成的目标");
                    return;
                }

                itemData.SpawnAsync(item =>
                {
                    GameUtils.GripItem(_casterRagdollHand, item);
                    GameUtils.StartCoroutine(SwordShoot.ItemExistenceTime, item.Despawn);
                    MyAssets.PlayAudio(MyAudios.ChuJian, _casterRagdollHand.transform.position);
                });
                break;
            }
            case GestureDirectionType.Downward:
            default:
                var offset = findHit
                    ? new Vector3(0, 0, 0)
                    : new Vector3(_spawnerPoint.forward.x, 0, _spawnerPoint.forward.z);
                offset *= DownShootDistance;
                offset.y += 1.5f;
                SwordShoot.ShootMulti(count, _spawnerPoint.position + offset,
                    throwVector.normalized, shootItemId, fixTarget);
                break;
        }
    }

    private int GetCount()
    {
        if (_chargeCounter == 0)
        {
            return 1;
        }

        var count = _chargeCounter * MaxShoot / _maxCountSeconds; // 只有6s
        if (count < 1) return 1;
        return count > MaxShoot ? MaxShoot : (int)count;
    }

    public override void Throw(Vector3 velocity)
    {
        base.Throw(velocity);
        Billing(true, throwVector: velocity);
    }


    private static void Log(object msg)
    {
        Debug.Log("Charge Shoot:" + msg);
    }
}